package com.ocean.system.utils;

import cn.hutool.core.util.RuntimeUtil;
import com.ocean.common.utils.FTPProperties;
import com.ocean.common.utils.StringUtils;
import com.ocean.system.domain.SysFTPConfigLeftJoinUser;
import com.ocean.system.domain.SysFTPLogftp;
import com.ocean.system.domain.SysOperLog;
import com.ocean.system.mapper.SysPostMapper;
import com.ocean.system.service.ISysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.List;

/**
 * @Author: FuGui Wang
 * @Description: ftp上传下载工具类（pom依赖冲突,所以放在system模块）
 * @Date 2023/3/3
 * @Version V1.1.0
 */
@Slf4j
@Component
public class FTPUtil {
    @Autowired
    private ISysOperLogService operLogService;
    @Autowired
    private SysPostMapper postMapper;

    public static FTPUtil ftpUtil;

    // 断点续传用户的文件路径
    private static String directory;

    public static String  one= "";//用户端,如果为空登录成功，不为空则出现错误
    public static String two = "";//客户端，如果为空登录成功，不为空则出现错误

    @PostConstruct
    public void init() {
        ftpUtil = this;
        ftpUtil.postMapper = this.postMapper;
    }

   /* public static void main(String[] args) {
        String s = "D:/cloud/ocean/NMF/BEN/OSM/CSDT/nccj/20250214/NMF_BEN_OSM_CSDT_202502142000_120001_SSW_00_L0.nc";
    }*/
    /**
     * 从ftp服务端下载到ftp客户端
     *
     * @param host         服务器ip
     * @param port         服务器端口
     * @param userName     账号名
     * @param password     密码
     * @param resourcePath 源文件路径
     * @param ftpDir       客户端存放路径
     * @return 执行状态
     */
    public boolean readConfigFileForFTP(String host, int port, String userName, String password, String resourcePath, String ftpDir, String userId, String downOrderId,Integer num,Integer muluyn) {
        // 创建返回状态
        boolean returnStatus = true;
        // 创建返回失败原因
        String returnCause = "";
        // 创建失败日志
        String reply = "";
        // 创建是否比对文件大小参数
        boolean boo = true;
        // 传输返回状态
        boolean status = false;
        // 创建OutputStream
        OutputStream is = null;
        // 获取开始时间
        long startTime = System.currentTimeMillis();
        /*System.setProperty("java.net.preferIPv4Stack", "true");*/
        SysFTPLogftp ftpLog1 = new SysFTPLogftp();
        // 创建ftp客户端
        FTPClient ftpOne = new FTPClient();
            ftpOne.setDefaultTimeout(10000);//设置超时时间为10s
        FTPClient ftpTwo = new FTPClient();
            ftpOne.setDefaultTimeout(10000);//设置超时时间为10s
        // 创建比较文件的变量，用于确保传输日志的正确性
        // 原始文件的大小
        long oneSize = 0;
        // 传输后文件的大小
        long twoSize = 0;
        String newFtpDir = "";
        // 1.传输数据
        try {
            // 登录并返回状态
            boolean loginStatus = loginFTP(ftpOne, ftpTwo, host, port, userName, password);
            // 判断登录状态
            if (loginStatus) {
                // 设置ftp的相关参数
                setClientConfig(ftpOne,ftpTwo);
                // 获取ftpOne目录下的文件
                FTPFile[] file = ftpOne.listFiles(resourcePath);
                // 获取到的数据不能为空
                if (file.length > 0) {
                    // 设置获取到的文件大小
                    oneSize = file[0].getSize();
                    // 设置编码
                    new String(file[0].getName().getBytes(), "UTF-8");
                    // 获取需要创建的目录
                    String substring1 = resourcePath.substring(0, resourcePath.lastIndexOf("/"));
                    String[] split = file[0].getName().split("_");
                    String mulu = split[0]+"/"+split[1]+"/"+split[2]+"/"+split[3];
                    if(resourcePath.contains("cut_data")){
                        if(muluyn == 0){
                            //是nc裁剪文件
                            newFtpDir = ftpDir+"/"+mulu+"/cut_data";
                        }else{
                            //是nc裁剪文件
                            newFtpDir = ftpDir+"/cut_data";
                        }

                    }else{
                        if(muluyn == 0){
                            newFtpDir = ftpDir+"/"+mulu;//用户自定义的目录
                        }else{
                            newFtpDir = ftpDir;//用户自定义的目录
                        }

                    }
                    // 处理业务,获取用户自定义路径
                    FTPUtil.makeDir(ftpTwo, newFtpDir);
                    // 客户端生成文件的路径
                    String userPath = newFtpDir + "/" + file[0].getName();
                    log.info("客户端生成文件的路径：" + userPath);
                    // 获取ftpTwo输出流
                    is = ftpTwo.storeFileStream(userPath);

                    // 切换目录
                    ftpOne.changeWorkingDirectory(resourcePath.substring(0, resourcePath.lastIndexOf("/")));
                    // 通过流把FTPOne复制到FTPTwo
                    if (null != is) {
                        ftpLog1.setDownOrderId(downOrderId);
                        ftpLog1.setServerIp(host);
                        ftpLog1.setServerPort(port);
                        ftpLog1.setFtpName(userName);
                        ftpLog1.setResourcePath(resourcePath);
                        ftpLog1.setFtpDir(ftpDir);
                        ftpLog1.setUserId(userId);
                        ftpLog1.setStatus("2");//正在上传
                        ftpLog1.setSize(Double.parseDouble(String.valueOf(oneSize)));
                        ftpUtil.postMapper.insertFTPLog(ftpLog1);//测试推送状态，有问题可注释

                        status = ftpOne.retrieveFile(file[0].getName(), is);
                        if(status == true){
                            ftpLog1.setId(ftpLog1.getId());
                            ftpLog1.setStatus("0");//
                            ftpUtil.postMapper.upFTPLog(ftpLog1);//测试推送状态，有问题可注释
                        }
                        is.close();
                    }
                } else {
                    //log.error("通过路径获取的文件数为0");
                    returnStatus = false;
                    returnCause = "通过路径获取的文件数为0,"+resourcePath+"路径下未找到";
                    reply = ftpOne.getReplyString();
                }
            } else {
                //log.error("登录连接失败&账号密码错误");
                returnStatus = false;
                returnCause = "**登录连接失败&账号密码错误 "+FTPUtil.one+FTPUtil.two;
            }
        } catch (Exception e) {
            boo = false;
            returnStatus = false;
            if (null == is) {
                returnCause += "OutputStream is为空，"+resourcePath+"路径下未找到，"+e;
            } else{
                returnCause += "被try catch捕获异常"+e;
            }
            reply = "ftpOne:" + ftpOne.getReplyString() + "  ftpTwo:" + ftpTwo.getReplyString() + "  e.toString()日志报错:"+e.toString();
            e.printStackTrace();
        } finally {
            try {
                if (status) {
                    log.info("数据传输运行时间：" + (System.currentTimeMillis() - startTime) + "ms");
                    // 完成暂挂命令
                    ftpTwo.completePendingCommand();
                } else if (null != is) {
                    boo = false;
                    returnStatus = false;
                    //log.error("传输文件后服务端返回状态显示未成功");
                    returnCause += "  **传输文件后服务端返回状态显示未成功";
                    if(StringUtils.isBlank(returnCause)){
                        returnCause += "  **传输文件后服务端返回状态显示未成功";
                    }
                } if (null == is) {
                    boo = false;
                    returnStatus = false;
                    //log.error("OutputStream is为空");
                    returnCause += "OutputStream is为空";
                    if(StringUtils.isBlank(returnCause)){
                        returnCause += "  **OutputStream is为空";
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 退出登录
            logout(ftpOne,ftpTwo);
        }
        try {
            // 2.重新登录比对文件大小
            if(boo){
                loginFTP(ftpOne, ftpTwo, host, port, userName, password);
                // 设置ftp的相关参数
                setClientConfig(ftpOne,ftpTwo);
                FTPFile[] file = ftpOne.listFiles(resourcePath);
                // 获取传输后的文件大小
                FTPFile[] file1 = ftpTwo.listFiles(newFtpDir + "/" + file[0].getName());
                twoSize = file1[0].getSize();
                // 判断文件大小是否一致
                if(oneSize != 0 && twoSize != 0){
                    if(oneSize != twoSize){
                        //log.error("传输后文件大小不一致");
                        returnStatus = false;
                        returnCause += "  **客户端:传输后文件大小不一致";
                    } else {
                        returnStatus = true;
                        reply = "";
                        returnCause += "";
                    }
                }
            }
            log.error("第三方接口返回："+reply);
            // 打印错误原因
            log.info(returnCause);
            // 3.保存记录到数据库
            SysFTPLogftp ftpLog = new SysFTPLogftp();
            if(1 == num){
                //ftpLog.setRemark("此条为重新上传的数据");
            } else if(2 == num) {
                // 修改当天传输失败的数据状态
                postMapper.updateErrorData(downOrderId);
                ftpLog.setRemark("此条为重新上传的数据");
            }

           /* if(ftpLog1.getId()!=0){
                ftpLog.setId(ftpLog1.getId());
            }else{*/
                ftpLog.setDownOrderId(downOrderId);
                ftpLog.setServerIp(host);
                ftpLog.setServerPort(port);
                ftpLog.setFtpName(userName);
                ftpLog.setResourcePath(resourcePath);
                ftpLog.setFtpDir(ftpDir);
                ftpLog.setUserId(userId);
            //}
            // 传输状态
            if (returnStatus) {
                ftpLog.setStatus("0");
            } else {
                ftpLog.setStatus("1");
            }
            // 失败原因
            if (StringUtils.isNotBlank(returnCause)) {
                ftpLog.setCause(returnCause);
            }
            // reply
            if (StringUtils.isNotBlank(reply)) {
                ftpLog.setReply(reply);
            }
            SysFTPLogftp sftp = new SysFTPLogftp();
            sftp.setResourcePath(ftpLog.getResourcePath());//防止重复文件添加
            List<SysFTPLogftp> sysFTPLogftps = ftpUtil.postMapper.selectRecordList(sftp);
            if(sysFTPLogftps.size()>0){
                SysFTPLogftp sysFTPLogftp = sysFTPLogftps.get(0);
                sysFTPLogftp.setStatus(ftpLog.getStatus());
                ftpUtil.postMapper.upFTPLog(sysFTPLogftp);
            }else{
                ftpUtil.postMapper.insertFTPLog(ftpLog);
            }
            //ftpUtil.postMapper.upFTPLog(ftpLog);
            // 重新连接再关闭，防止报错
            ftpOne.connect(FTPProperties.FTP_IP, FTPProperties.FTP_PORT);
            // 退出登录
            logout(ftpOne,ftpTwo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return returnStatus;
    }



    /**
     * 退出登录
     */
    private void logout(FTPClient ftpOne,FTPClient ftpTwo) {
        try {
            ftpOne.logout();
            if (ftpOne.isConnected()) {
                ftpOne.disconnect();
            }
            if (ftpTwo.isConnected()) {
                ftpTwo.disconnect();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * ftp创建目录——ftpClient只支持一级一级创建
     *
     * @param ftp  ftp客户端
     * @param path 路径
     * @return 创建成功状态
     */
    private static boolean makeDir(FTPClient ftp, String path) throws IOException {
        // 分割
        String[] paths = path.split("/");
        // 创建成功标识
        boolean isMakeSuccess = false;
        // 遍历每一级路径
        for (String str : paths) {
            // 切换目录，根据切换是否成功判断子目录是否存在
            boolean changeSuccess = ftp.changeWorkingDirectory(str);
            // 该级路径不存在就创建并切换
            if (!changeSuccess) {
                isMakeSuccess = ftp.makeDirectory(str);
                ftp.changeWorkingDirectory(str);
            }
        }
        return isMakeSuccess;
    }

    /**
     * 登录ftp
     *
     * @param ftpOne FTP服务端
     * @param ftpTwo FTP客户端
     * @return 登录状态
     */
    private static boolean loginFTP(FTPClient ftpOne, FTPClient ftpTwo, String host, int port, String
            userName, String password) throws IOException {
        try {

            if(!ftpOne.isConnected()){
                ftpOne.connect(FTPProperties.FTP_IP, FTPProperties.FTP_PORT);
            }

        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        //连接FTPOne
        boolean isLoginOne = ftpOne.login(FTPProperties.FTP_USERNAME, FTPProperties.FTP_PASSWORD);
        if (!FTPReply.isPositiveCompletion(ftpOne.getReplyCode())) {
            log.info("服务端:FTP连接失败，用户名或密码错误");
            one = "    **系统配置wdz账号登录报错：服务端:FTP连接失败，用户名或密码错误 ";
        } else {
            log.info(FTPProperties.FTP_IP + " FTP服务端连接成功");
        }

        //连接FTPTwo
        try {
            if(!ftpTwo.isConnected()){
                ftpTwo.connect(host, port);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        boolean isLoginTwo = ftpTwo.login(userName, password);
        if (!FTPReply.isPositiveCompletion(ftpTwo.getReplyCode())) {
            log.info("客户端:FTP连接失败，用户名或密码错误");
            two = "    **推送端账户未连接成功，账号登录报错：客户端:FTP连接失败，用户名或密码错误";
        } else {
            log.info(host + " FTP客户端连接成功");
        }
        // 判断是否连接成功
        if (isLoginOne && isLoginTwo) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 设置ftp的相关参数
     *
     * @param ftpOne FTP服务端
     * @param ftpTwo FTP客户端
     */
    private void setClientConfig(FTPClient ftpOne, FTPClient ftpTwo) {
        try {
            // 设置被动模式
            ftpOne.enterLocalPassiveMode();
            ftpTwo.enterLocalPassiveMode();
            // 设置文件类型
            ftpOne.setFileType(FTP.BINARY_FILE_TYPE);
            ftpTwo.setFileType(FTP.BINARY_FILE_TYPE);

            ftpOne.setControlEncoding("UTF-8");
            ftpTwo.setControlEncoding("UTF-8");

            // 设置缓冲区(解决上传慢问题)
            ftpOne.setBufferSize(1024 * 1024 * 10);
            ftpTwo.setBufferSize(1024 * 1024 * 10);

            // 设置传输方式为流方式
            ftpOne.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
            ftpTwo.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);

            // 设置建立连接过程中的超时，单位 ms；0表示无限大
                /*ftpOne.setConnectTimeout(10000);
                ftpTwo.setConnectTimeout(10000);*/
            ftpOne.setConnectTimeout(3600000);
            ftpTwo.setConnectTimeout(3600000);

            // 被动模式下，用于设置传输数据的 Socket 的 SoTimeout，单位 ms；0表示无限大
            ftpOne.setDataTimeout(3600000);
            ftpTwo.setDataTimeout(3600000);

            // 用于在传输数据过程中，命令连接发送心跳数据的超时时间，防止被路由器干掉，单位 s
            ftpOne.setControlKeepAliveTimeout(60);
            ftpTwo.setControlKeepAliveTimeout(60);

            // 读取数据时阻塞链路的超时时间，如果不设置，可能在网络波动时无限时阻塞在此，保证程序不卡死
            ftpOne.setSoTimeout(60000);
            ftpTwo.setSoTimeout(60000);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 断点续传
     * @param resourcePath 文件路径路径
     * @param serverIp 服务器IP
     * @param serverPort 端口
     * @param FtpName ftp用户名
     * @param ftpPassword ftp密码
     * @param ftpDir ftp位置路径
     * @param userId 用户id
     * @param downOrderId 下载单id
     * @return 上传状态
     */
    public boolean uploadFileBreakPoint(String resourcePath,String serverIp,Integer serverPort,String FtpName,String ftpPassword,String ftpDir,String userId,String downOrderId) {
        // 创建ftp客户端
        FTPClient ftpOne = new FTPClient(); // 源服务器
        FTPClient ftpTwo = new FTPClient(); // 用户服务器
        // 用户服务器文件目录
        String newFtpDir = "";
        try {
            // 连接登录
            boolean loginStatus = loginFTP(ftpOne, ftpTwo, serverIp, serverPort, FtpName, ftpPassword);
            if(loginStatus){
                setClientConfig(ftpOne,ftpTwo);
                // 获取ftpOne目录下的文件
                FTPFile[] file = ftpOne.listFiles(resourcePath);
                if (file.length > 0) {
                    // 设置编码
                    new String(file[0].getName().getBytes(), "UTF-8");
                    // 拼接用户服务器目录
                    String substring1 = resourcePath.substring(0, resourcePath.lastIndexOf("/"));
                    newFtpDir = ftpDir + substring1.substring(12, substring1.length());
                    // 客户端生成文件的路径
                    String userPath = newFtpDir + "/" + file[0].getName();
                    log.info(String.valueOf("原文件大小："+file[0].getSize()));
                    // 检查ftp服务器文件是否存在
                    FTPFile[] listFiles = ftpTwo.listFiles(userPath);
                    // 获取的文件必须大于0
                    if (listFiles.length > 0) {
                        // 判断服务器文件是否大于要上传文件，大于不上传，小于则上传
                        if (listFiles[0].getSize() >= file[0].getSize()) {
                            // 不需要断点传输
                            return true;
                        } else {
                            try {
                                // 保存记录到数据库
                                SysFTPLogftp ftpLog = new SysFTPLogftp();
                                // 修改当天传输失败的数据状态
                                postMapper.updateErrorData(downOrderId);
                                ftpLog.setRemark("此条断点续传数据");
                                ftpLog.setDownOrderId(downOrderId);
                                ftpLog.setServerIp(serverIp);
                                ftpLog.setServerPort(serverPort);
                                ftpLog.setFtpName(FtpName);
                                ftpLog.setResourcePath(resourcePath);
                                ftpLog.setFtpDir(ftpDir);
                                ftpLog.setUserId(userId);
                                // 传输状态
                                ftpLog.setStatus("0");
                                ftpLog.setIsBreakpointTransmission("1");
                                ftpUtil.postMapper.insertFTPLog(ftpLog);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

                            // 切换目录
                            ftpTwo.changeWorkingDirectory(newFtpDir);
                            File sourceFile =new File(resourcePath);
                            // 开始执行断点续传
                            log.info("断点传输开始执行从"+sourceFile+"传到"+userPath);
                            resumeBreakPoint(ftpOne, ftpTwo, userPath, sourceFile, listFiles);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 退出登录
            logout(ftpOne,ftpTwo);
        }
        return false;
    }

    /**
     * 断点上传代码段
     * @param ftpOne 源数据服务器
     * @param ftpTwo 用户服务器
     * @param userPath 用户服务器路径
     * @param file 文件路径
     * @param listFiles 获取到的数据
     * @return 上传状态
     */
    private boolean resumeBreakPoint(FTPClient ftpOne,FTPClient ftpTwo,String userPath, File file, FTPFile[] listFiles) throws IOException {
        boolean status;
        // 服务器文件小于将要上传文件，进行断点续传
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        OutputStream out = ftpTwo.appendFileStream(userPath);
        // 断点续传
        ftpTwo.setRestartOffset(listFiles[0].getSize());
        raf.seek(listFiles[0].getSize());

        byte[] bytes = new byte[1024];
        int c;
        while ((c = raf.read(bytes)) != -1) {
            out.write(bytes, 0, c);
        }
        out.flush();
        raf.close();
        out.close();
        status = ftpTwo.completePendingCommand();
        // 退出登录
        logout(ftpOne,ftpTwo);
        return status;
    }

    /**
     * FTP登录账号手动下载，此程序做查询推送硬链接
     * @param sysFTPConfigLeftJoinUser 数据实体对象
     */
    public boolean manualDownload(SysFTPConfigLeftJoinUser sysFTPConfigLeftJoinUser) {
        String log1 = "";
        // 创建ftp客户端
        FTPClient ftpOne = new FTPClient(); // 源服务器
        // 生成硬链接的字符串
        String hardLink = "ln ";
        // 创建基础用户目录
        String baseDirectory = "";
        //if(StringUtils.isNotBlank(sysFTPConfigLeftJoinUser.getFtpName())){
            baseDirectory = "/cloud/ftpusr/ocean/user/"+sysFTPConfigLeftJoinUser.getUserName();
            log1 += "开始创建硬链接地址:"+baseDirectory;
        /*} else {
            // ftp名为空直接返回
            return false;
        }*/
        try {
            // 连接服务器
                if(!ftpOne.isConnected()){
                    ftpOne.connect(FTPProperties.FTP_IP, FTPProperties.FTP_PORT);
                }
                //连接FTPOne
                boolean loginStatus = ftpOne.login(FTPProperties.FTP_USERNAME, FTPProperties.FTP_PASSWORD);
                if(!loginStatus){
                    return false;
                }
                if (!FTPReply.isPositiveCompletion(ftpOne.getReplyCode())) {
                    log.info("服务端:FTP连接失败，用户名或密码错误");
                    log1 += "服务端:FTP连接失败，用户名或密码错误";
                    return false;
                } else {
                    log.info(FTPProperties.FTP_IP + " FTP服务端连接成功");
                    log1 += "1.FTP服务端连接成功";
                }
            // 进入基础文件目录
            boolean changeStatus = ftpOne.changeWorkingDirectory(baseDirectory);
            // 判断是否进入目录成功
            if(changeStatus){
                // 创建文件目录
                String substring = sysFTPConfigLeftJoinUser.getResourcePath().substring(0, sysFTPConfigLeftJoinUser.getResourcePath().lastIndexOf("/"));
                log1 += "2.创建文件目录";
                // 处理业务,获取用户自定义路径
                FTPUtil.makeDir(ftpOne, substring);
                log1 += "3.处理业务,获取用户自定义路径";
                // 处理文件路径，拼接硬链接
                //String replace = sysFTPConfigLeftJoinUser.getResourcePath().replace("cloud", "ftpusr");
                String replace = sysFTPConfigLeftJoinUser.getResourcePath();//存储地址
                log1 += "4.处理文件路径，拼接硬链接";
                hardLink = hardLink + replace + " /cloud/ftpusr/ocean/user/"+ sysFTPConfigLeftJoinUser.getUserName() + sysFTPConfigLeftJoinUser.getResourcePath();
                // 生成硬链接
                log.info("生成硬链接"+sysFTPConfigLeftJoinUser.getUserName()+","+hardLink);
                log1 += "5.生成硬链接"+sysFTPConfigLeftJoinUser.getUserName()+","+hardLink;
                Process exec = RuntimeUtil.exec(hardLink);
            } else{
                log.info("没有创建用户目录，联系管理员创建账号");
                log1 += "没有创建用户目录，联系管理员创建账号";
                return false;
            }
            SysOperLog slog = new SysOperLog();
            slog.setTitle("硬链接");
            slog.setBusinessType(0);//其他业务
            slog.setRequestMethod("内部定时方法");
            slog.setOperatorType(1);
            slog.setOperName("内部定时方法");
            slog.setJsonResult(log1);
            //operLogService.insertOperlog(slog);
            System.out.println(log1);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭连接
            try {
                ftpOne.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
}
